#include "cpoproc.h"
#include "cpodata.h"
#include "cpofsm.h"
#include "cpotimer.h"
#include "cpolist.h"
#include "es_print.h"
#include "cpomqtt.h"
#include <string.h>
#include <mosquitto.h>
#include <json-c/json_util.h>
#include <json-c/json.h>

extern struct mosquitto *mqtt_cpo;
extern cpo_dev_t *cpo_dev;
extern cpo_fsm_head_l *cpo_subfsm_hd;
extern cpo_fsmsets_t cpo_subfsmset[];
extern struct _dk_repo dk_repo;
extern pthread_t tid_do_after_stop;

void print_pointprop_info(int sn, point_prop_t *pp)
{
    enum type_of_val tp;
    tp = pp->tag;

    switch(tp){
        case TypeOfVal_INT:
            printf("sn = %d, \t name = %-13s \t dk = %s \t func = %-10s \t val = %d \n", sn, pp->name, pp->dev_key, pp->func, pp->val.i);
            break;
        case TypeOfVal_BOOL:
            printf("sn = %d, \t name = %-13s \t dk = %s \t func = %-10s \t val = %d \n", sn, pp->name, pp->dev_key, pp->func, pp->val.b);
            break;
        case TypeOfVal_DOUBLE:
            printf("sn = %d, \t name = %-13s \t dk = %s \t func = %-10s \t val = %lf \n", sn, pp->name, pp->dev_key, pp->func, pp->val.d);
            break;
    
        default:
            break;
    }
}

/* 调试用，打印所有点位信息，验证点位正确性
 * PS: 刚addDevice时, 实点的value还没有值, 忽略打印结果
 */
void print_all_points()
{

    int i;
    wp_dev_t *pos = NULL;
    // 1. pchwp
    // real points
    printf("pchwp >>>>>>>>>>>>>>>>>>>> \n");
    list_for_each_entry(pos, &cpo_dev->pchwp_head.list, list){
        for(i=0; i<pos->wp_prop.points_num; i++){
            print_pointprop_info(pos->sn, pos->wp_prop.points+i);
        }
    }
    // 2. cwp
    // real points
    pos = NULL;
    printf("cwp >>>>>>>>>>>>>>>>>>>> \n");
    list_for_each_entry(pos, &cpo_dev->cwp_head.list, list){
        for(i=0; i<pos->wp_prop.points_num; i++){
            print_pointprop_info(pos->sn, pos->wp_prop.points+i);
        }
    }
    // 3. ch
    ch_dev_t *ch_pos;
    printf("ch >>>>>>>>>>>>>>>>>>>> \n");
    list_for_each_entry(ch_pos, &cpo_dev->ch_head.list, list){
        for(i=0; i<ch_pos->ch_prop.points_num; i++){
            print_pointprop_info(ch_pos->sn, ch_pos->ch_prop.points+i);
        }
    }
    printf("chp >>>>>>>>>>>>>>>>>>>> \n");
    for(i=0; i<cpo_dev->ch_head.chg_rp.points_num; i++){
        print_pointprop_info(-1, cpo_dev->ch_head.chg_rp.points+i);
    }

    // 4. ct
    ct_dev_t *ct_pos;
    ctf_dev_t *ctf_pos;
    int j;
    printf("ct >>>>>>>>>>>>>>>>>>>> \n");
    list_for_each_entry(ct_pos, &cpo_dev->ct_head.list, list_ct){
        //塔的实点
        for(i=0; i<ct_pos->ct_prop.points_num; i++){
            print_pointprop_info(ct_pos->sn, ct_pos->ct_prop.points+i);
        }
        printf("ctf >>>>>>>>>>>>>>>>>>>> \n");
        //塔风扇的实点
        list_for_each_entry(ctf_pos, &ct_pos->list_ctf_hd, list){
            for(j=0; j<ctf_pos->ctf_prop.points_num; j++){
                print_pointprop_info(ctf_pos->sn, ctf_pos->ctf_prop.points+j);
            }
        }
    }
    //塔组的实点
    printf("ctg >>>>>>>>>>>>>>>>>>>> \n");
    for(i=0; i<cpo_dev->ct_head.ctg_rp.points_num; i++){
        print_pointprop_info(-1, cpo_dev->ct_head.ctg_rp.points+i);
    }

    // 5. 冷源系统
    printf("sys rp >>>>>>>>>>>>>>>>>>>> \n");
    for(i=0; i<cpo_dev->sys_rp.points_num; i++){
        print_pointprop_info(-1, cpo_dev->sys_rp.points+i);

    }
    printf("sys vp >>>>>>>>>>>>>>>>>>>> \n");
    for(i=0; i<cpo_dev->sys_vp.points_num; i++){
        print_pointprop_info(-1, cpo_dev->sys_vp.points+i);
    }
}

/* 给初始化设备实点位属性, 并订阅点位, 并将topic保存到dk_repo中
 *   name                 jdk                          jmk
 *    |                     |                          |
 * "PCHWP-C":{"deviceKey":"23016", "jsonMap":{"key":"ch16"}}
 * @point：被初始化的设备
 * @name：json中提取的点位name
 * @jdk：json中提取的点位deviceKey对象
 * @jmk：json中提取的点位jsonMap/key对象
 */
static void extract_rp_2pointprop(point_prop_t *point, char *name, struct json_object *jdk, struct json_object * jmk)
{
    strcpy(point->name, name);
    strcpy(point->dev_key, json_object_get_string(jdk));
    strcpy(point->func, json_object_get_string(jmk));

    char topic[128];
    sprintf(topic, "%s/%s", MQTT_PUB_PREFIX, point->dev_key);
    mosquitto_subscribe(mqtt_cpo, NULL, topic, 0);

    dk_repo.num++;
    dk_repo.ptr = realloc(dk_repo.ptr, sizeof(ARRAY128)*dk_repo.num);
    strcpy((char *)(dk_repo.ptr+dk_repo.num-1), topic);
}

/* 给初始化设备虚点位属性
 *   name       jval
 *    |         |
 * "CH-MAX-OP":0.0
 * @point：被初始化的设备
 * @name：json中提取的点位name
 * @jval：json中提取的点位value
 */
static void extract_vp_2pointprop(point_prop_t *point, char *name, struct json_object *jval)
{
    strcpy(point->dev_key, cpo_dev->device_id);
    strcpy(point->name, name);
    strcpy(point->func, name);

    switch(point->tag){
        case TypeOfVal_BOOL:    point->val.b = json_object_get_boolean(jval);   break;
        case TypeOfVal_INT:     point->val.i = json_object_get_int(jval);       break;
        case TypeOfVal_DOUBLE:  point->val.d = json_object_get_double(jval);    break;
        default: break;
    }
}

/* 提取实点点位组
 * @jele：被提取的设备json报文，一个包含多个实点的对象，比如：
 * e.g.
 * {
    "PCHWP-C":{"deviceKey":"23016", "jsonMap":{"key":"ch16"}},
    "PCHWP-S":{"deviceKey":"23017", "jsonMap":{"key":"ch17"}},
    ...
   }

 * @dev_prop：设备描述符，jele中的内容转存到dev_prop指向的内存 
 */
static void extract_cpo_rpg(struct json_object *jele, dev_prop_t *dev_prop)
{
    int i;
    struct json_object *jdeviceKey, *jmap, *jmapKey;
    json_object_object_foreach(jele,key,jval){

        jdeviceKey = json_object_object_get(jval, "deviceKey");
        jmap = json_object_object_get(jval, "jsonMap");
        jmapKey = json_object_object_get(jmap, "key");
        /* 跳过这种没有选中的点位
         * "xxxx":{"deviceKey":"", "jsonMap":{"key":""}}, 
         */
        if(strcmp(json_object_get_string(jdeviceKey), "") == 0
            || strcmp(json_object_get_string(jmapKey), "") == 0){
            continue;
        }

        dev_prop->points_num++;
        /* 每解析一个新实点, 就扩充一段内存用来保存新实点的信息 */
        dev_prop->points = realloc(dev_prop->points, sizeof(point_prop_t) * dev_prop->points_num);
        i = dev_prop->points_num - 1;
        extract_rp_2pointprop(dev_prop->points+i, key, jdeviceKey, jmapKey);

    }
#if 0
for(i=0; i<dev_prop->points_num; i++){
    ES_PRT_DEBUG("name = %s, device_key = %s, func = %s \n", \
                (dev_prop->points+i)->name, \
                (dev_prop->points+i)->dev_key, \
                (dev_prop->points+i)->func);
}
#endif
}

/* 根据configuration中的实点添加初级冷冻水泵
 * @jpchwp: 是个数组对象，其中包含pchwp_num个水泵设备
 * e.g.
 * [
    {   "PCHWP-C":{"deviceKey":"23016", "jsonMap":{"key":"ch16"}}   },  // 第一个水泵
    {   "PCHWP-C":{"deviceKey":"23017", "jsonMap":{"key":"ch17"}}   }   // 第二个水泵
 * ]
 */
static void add_cpo_pchwp(struct json_object *jpchwp)
{
    int pchwp_num;  // 初级冷冻水泵个数
    pchwp_num = json_object_array_length(jpchwp);
    ES_PRT_INFO("Cpo: pchwp num = %d \n", pchwp_num);

    cpo_dev->pchwp_head.pchwp_num = pchwp_num;

    int i;
    wp_dev_t *p;
    struct json_object *jele;

    pthread_mutex_lock(&cpo_dev->pchwp_head.lock);

    /* 根据数组长度, 创建设备, 并初始化每个设备 */
    for(i=0; i<pchwp_num; i++){
        /* get 数组中第i个成员对象 */
        jele = json_object_array_get_idx(jpchwp, i);
        /* create 一个水泵设备 */
        p = malloc(sizeof(wp_dev_t)*1);
        memset(p, 0, sizeof(wp_dev_t));
        p->sn = i;
        /* 实点组 */
        extract_cpo_rpg(jele, &p->wp_prop);
        /* 此处要注意插入顺序 */
        list_add_tail(&p->list, &cpo_dev->pchwp_head.list);
    }
    pthread_mutex_unlock(&cpo_dev->pchwp_head.lock);
    ES_PRT_INFO("Successfully add %d pchwp \n", pchwp_num);
}

/* 根据configuration中的实点添加冷却水泵
 * @jcwp: 是个数组对象，其中包含cwp_num个水泵设备
 */
static void add_cpo_cwp(struct json_object *jcwp)
{
    int cwp_num;  // 初级冷冻水泵个数
    cwp_num = json_object_array_length(jcwp);
    ES_PRT_INFO("Cpo: cwp num = %d \n", cwp_num);

    cpo_dev->cwp_head.cwp_num = cwp_num;

    int i;
    wp_dev_t *p;
    struct json_object *jele;

    pthread_mutex_lock(&cpo_dev->cwp_head.lock);

    /* 根据数组长度, 创建设备, 并初始化每个设备 */
    for(i=0; i<cwp_num; i++){
        /* get 数组中第i个成员对象 */
        jele = json_object_array_get_idx(jcwp, i);
        /* create 一个水泵设备 */
        p = malloc(sizeof(wp_dev_t)*1);
        memset(p, 0, sizeof(wp_dev_t));
        p->sn = i;
        /* 实点组 */
        extract_cpo_rpg(jele, &p->wp_prop);
        /* 此处要注意插入顺序 */
        list_add_tail(&p->list, &cpo_dev->cwp_head.list);
    }
    pthread_mutex_unlock(&cpo_dev->cwp_head.lock);
    ES_PRT_INFO("Successfully add %d cwp \n", cwp_num);
}

/* @jch: 冷机数组对象:
 * e.g.
 * "CH": [
 *      //设备1
 *      { "CH-C": {"deviceKey":"23001", "jsonMap":{"key":"ch1"}}, //冷机启停开关 },
 *      //设备2
 *      { "CH-C": {"deviceKey":"23001", "jsonMap":{"key":"ch1"}}, //冷机启停开关 },
 *  ]
 */
static void _add_cpo_chg(struct json_object *jch)
{
    int ch_num;  // 冷机个数
    ch_num = json_object_array_length(jch);
    ES_PRT_INFO("Cpo: ch_num = %d \n", ch_num);

    cpo_dev->ch_head.ch_num = ch_num;

    int i;
    ch_dev_t *p;
    struct json_object *jele;

    /* 根据数组长度, 创建设备, 并初始化每个设备 */
    for(i=0; i<ch_num; i++){
        /* get 数组中第i个成员对象 */
        jele = json_object_array_get_idx(jch, i);
        /* create 一个冷机设备 */
        p = malloc(sizeof(ch_dev_t)*1);
        memset(p, 0, sizeof(ch_dev_t));
        p->sn = i;
        /* 实点组 */
        extract_cpo_rpg(jele, &p->ch_prop);
        /* 此处要注意插入顺序 */
        list_add_tail(&p->list, &cpo_dev->ch_head.list);
    }
}

/* 根据configuration中的实点添加冷机
 * @jchg: 冷机组对象
 */
static void add_cpo_chg(struct json_object *jchg)
{
    pthread_mutex_lock(&cpo_dev->ch_head.lock);

    json_object_object_foreach(jchg, key, jval){
        if(strcmp(key, CH) == 0){
            _add_cpo_chg(jval);

        /* 提取冷机组的点位 */
        }else if(strcmp(key, CHG_SYS) == 0){
            extract_cpo_rpg(jval, &cpo_dev->ch_head.chg_rp);
        }
    }
    pthread_mutex_unlock(&cpo_dev->ch_head.lock);
    ES_PRT_INFO("Successfully add %d ch \n", cpo_dev->ch_head.ch_num);
}

/* 添加冷却塔风扇 
 * @jval:
 * [
 *  {}  // 风扇1
 *  {}  // 风扇2
 * ]
 * 
 */
static void add_cpo_ctf(struct json_object *jval, ct_dev_t *ctf_head)
{
    int ctf_num;  // 冷却塔风扇个数
    ctf_num = json_object_array_length(jval);
    ES_PRT_INFO("Cpo: ctf_num = %d \n", ctf_num);

    ctf_head->ctf_num = ctf_num;

    int i;
    ctf_dev_t *p;
    struct json_object *jele;

    /* 根据数组长度, 创建设备, 并初始化每个设备 */
    for(i=0; i<ctf_num; i++){
        /* get 数组中第i个成员对象 */
        jele = json_object_array_get_idx(jval, i);
        /* create 一个设备 */
        p = malloc(sizeof(ctf_dev_t)*1);
        memset(p, 0, sizeof(ctf_dev_t));
        p->sn = i;
        /* 实点组 */
        extract_cpo_rpg(jele, &p->ctf_prop);
        /* 此处要注意插入顺序 */
        list_add_tail(&p->list, &ctf_head->list_ctf_hd);
    }
}

/* 添加单个冷却塔 
 * @jele:
 * {
        "CTF":[
            {}  // 风扇1
            {}  // 风扇1
        ]
    }
    @ct_dev: 冷却塔设备描述符
 */
static void add_cpo_ct(struct json_object *jele, ct_dev_t *ct_dev)
{
    pthread_mutex_lock(&ct_dev->lock);

    json_object_object_foreach(jele, key, jval){
        if(strcmp(key, CTF) == 0){
            /* 添加风扇 */
            add_cpo_ctf(jval, ct_dev);
        
        /* 提取冷却塔的点位 */
        }else if(strcmp(key, CT_SYS) == 0){
            extract_cpo_rpg(jval, &ct_dev->ct_prop);
        }
    }

    pthread_mutex_unlock(&ct_dev->lock);
}

/* @jct: 冷却塔数组对象:
 * 
 * "CT": [
 *   //塔1
 *   {}
 *   //塔2
 *   {}
 *  ]
 */
static void _add_cpo_ctg(struct json_object *jct)
{
    int ct_num;  // 冷却塔个数
    ct_num = json_object_array_length(jct);
    ES_PRT_INFO("Cpo: ct_num = %d \n", ct_num);

    cpo_dev->ct_head.ct_num = ct_num;

    int i;
    ct_dev_t *p;
    struct json_object *jele;

    /* 根据数组长度, 创建设备, 并初始化每个设备 */
    for(i=0; i<ct_num; i++){
        /* get 数组中第i个成员对象 */
        jele = json_object_array_get_idx(jct, i);
        /* create 一个冷却塔设备 */
        p = malloc(sizeof(ct_dev_t)*1);
        memset(p, 0, sizeof(ct_dev_t));
        p->sn = i;
        pthread_mutex_init(&p->lock, NULL);
        INIT_LIST_HEAD(&p->list_ctf_hd);
        /* 添加单个设备 */
        add_cpo_ct(jele, p);
        /* 此处要注意插入顺序 */
        list_add_tail(&p->list_ct, &cpo_dev->ct_head.list);
    }
}

/* 根据configuration中的实点添加冷却塔
 * @jctg: 冷却塔组对象
 * e.g.
 * "CTG":{
 *      ...
 * }
 */
static void add_cpo_ctg(struct json_object *jctg)
{
    pthread_mutex_lock(&cpo_dev->ct_head.lock);

    json_object_object_foreach(jctg, key, jval){
        if(strcmp(key, CT) == 0){
            _add_cpo_ctg(jval);

        /* 提取冷却塔组的点位 */
        }else if(strcmp(key, CTG_SYS) == 0){
            extract_cpo_rpg(jval, &cpo_dev->ct_head.ctg_rp);
        }
    }
    pthread_mutex_unlock(&cpo_dev->ct_head.lock);
    ES_PRT_INFO("Successfully add %d ct \n", cpo_dev->ct_head.ct_num);
}

/* 添加系统实点 */
static void add_cpo_sys_rp(struct json_object *jsys)
{
    pthread_mutex_lock(&cpo_dev->lock);
    extract_cpo_rpg(jsys, &cpo_dev->sys_rp);
    pthread_mutex_unlock(&cpo_dev->lock);
}

/* 根据configuration中的实点添加设备 */
static void add_cpo_dev_by_rp(struct json_object *jcfg)
{
    json_object_object_foreach(jcfg,key,jval){
        /* 添加初级冷冻水泵 */
        if(strcmp(key, PCHWP) == 0){
            add_cpo_pchwp(jval);
        /* 添加冷却水泵 */
        }else if(strcmp(key, CWP) == 0){
            add_cpo_cwp(jval);
        /* 添加冷机 */
        }else if(strcmp(key, CHG) == 0){
            add_cpo_chg(jval);
        /* 添加冷却塔 */
        }else if(strcmp(key, CTG) == 0){
            add_cpo_ctg(jval);
        /* 添加设备总点位 */
        }else if(strcmp(key, "SYS") == 0){
            add_cpo_sys_rp(jval);
        }
    }
}

/* 提取虚点组
 * @jele：被提取的设备json报文，一个包含多个实点的对象，比如：
 * e.g.
 * {
        "TEMP-ERR": 1.0,    //double 温度偏差
        "CH-RT":0,          //unsigned int 冷机运行时间
    }
 * @dev_prop：设备描述符，jele中的内容转存到dev_prop指向的内存 
 */
static void extract_cpo_vpg(struct json_object *jele, dev_prop_t *dev_prop)
{
    int i;
    enum type_of_val tp_val;

    json_object_object_foreach(jele, key, jval){

        switch(json_object_get_type(jval)){
            case json_type_null:    continue;  // 跳过null对象
            case json_type_int:     tp_val = TypeOfVal_INT;     break;
            case json_type_double:  tp_val = TypeOfVal_DOUBLE;  break;
            case json_type_boolean: tp_val = TypeOfVal_BOOL;    break;
            case json_type_string:  tp_val = TypeOfVal_STRING;  break;
            default:    break;
        }

        dev_prop->points_num++;
        /* 每解析一个新实点, 就扩充一段内存用来保存新实点的信息 */
        dev_prop->points = realloc(dev_prop->points, sizeof(point_prop_t) * dev_prop->points_num);
        i = dev_prop->points_num - 1;
        (dev_prop->points+i)->tag = tp_val;
        extract_vp_2pointprop(dev_prop->points+i, key, jval);
    }
}

/*水泵的变频相关的虚点配置
 * @jele: 形如下面的json
    {
    "PCHWP-IS-VSD":true, //bool true: 变频，false：定频，此点位不能为空，由平台给值
    "PCHWP-VSD-MAX":50.0,  //double 初级冷冻水泵频率上限，如果变频，那么此点位不能为空
    "PCHWP-VSD-MIN":30.0 //double 初级冷冻水泵频率下限，如果变频，那么此点位不能为空
    },

 * @sn: 设备序列号
 * @hd: 设备所在链表的链表头
 * @type: 区分冷冻水泵还是冷却水泵的字符串
*/
static void extract_cpo_wp_vp_vsd(struct json_object *jele, int sn, struct list_head *hd, char *type)
{
    wp_dev_t *pos;
    struct json_object *jvsd;

    if(strcmp(type, PCHWP) == 0){
        jvsd = json_object_object_get(jele, PCHWP_IS_VSD);
    }else if(strcmp(type, CWP) == 0){
        jvsd = json_object_object_get(jele, CWP_IS_VSD);
    }

    bool is_vsd;
    double max = -1;
    double min = -1;

    /* 提取json中变频相关的配置 */
    if(json_object_get_boolean(jvsd) == true){
        is_vsd = ISVSD;
        if(strcmp(type, PCHWP) == 0){
            max = json_object_get_double(json_object_object_get(jele, PCHWP_VSD_MAX));
            min = json_object_get_double(json_object_object_get(jele, PCHWP_VSD_MIN));
        }else if(strcmp(type, CWP) == 0){
            max = json_object_get_double(json_object_object_get(jele, CWP_VSD_MAX));
            min = json_object_get_double(json_object_object_get(jele, CWP_VSD_MIN));
        }
    }else{
        is_vsd = !ISVSD;
    }
    /* 找到设备, 配置变频变量 */
    list_for_each_entry(pos, hd, list){
        if(sn == pos->sn){
            pos->wp_prop.is_vsd = is_vsd;
            pos->wp_prop.vsd_max = max;
            pos->wp_prop.vsd_min = min;
        }
    }

}

/* 提取水泵的虚点
 * @jwp: 设备
 * @hd: 设备所在链表头
 * @type: 区分冷冻水泵还是冷却水泵的字符串
 */
static void extract_cpo_wp_vp(struct json_object *jwp, struct list_head *hd, char *type)
{
    int num;  // 水泵个数
    num = json_object_array_length(jwp);

    int i;
    struct json_object *jele;

    for(i=0; i<num; i++){
        jele = json_object_array_get_idx(jwp, i);
        extract_cpo_wp_vp_vsd(jele, i, hd, type);
    }
}

/*塔的变频相关的虚点配置
 * @jele: 形如下面的json
    {
    "CTF-IS-VSD":true, //1: 变频，0：定频，此点位不能为空，由平台给值
    "CTF-VSD-MAX":60.0,
    "CTF-VSD-MIN":30.0
    }

 * @sn: 塔的序列号
 * @hd: 设备所在链表的链表头
*/
static void extract_cpo_ct_vp_vsd(struct json_object *jele, int sn, struct list_head *hd)
{
    ct_dev_t *pos;
    struct json_object *jvsd;
    if( !json_object_object_get_ex(jele, CTF_IS_VSD, &jvsd) ){
        return;
    }
    bool is_vsd;
    double max = -1;
    double min = -1;

    /* 提取json中变频相关的配置 */
    if(json_object_get_boolean(jvsd) == true){
        is_vsd = ISVSD;
        max = json_object_get_double(json_object_object_get(jele, CTF_VSD_MAX));
        min = json_object_get_double(json_object_object_get(jele, CTF_VSD_MIN));
    }else{
        is_vsd = !ISVSD;
    }

    /* 找到设备, 配置变频变量 */
    list_for_each_entry(pos, hd, list_ct){
        if(sn == pos->sn){
            pos->ct_prop.is_vsd = is_vsd;
            pos->ct_prop.vsd_max = max;
            pos->ct_prop.vsd_min = min;
        }
    }
}

/* 提取塔的虚点
 * @jct: 设备
 * @hd: 设备所在链表头
 */
static void extract_cpo_ct_vp(struct json_object *jct, struct list_head *hd)
{
    int num;  // 塔个数
    num = json_object_array_length(jct);

    int i;
    struct json_object *jele;

    for(i=0; i<num; i++){
        jele = json_object_array_get_idx(jct, i);
        extract_cpo_ct_vp_vsd(jele, i, hd);
    }
}

/* 提取冷机的虚点
 * @jele: 
 * 		{
			"CH-REAL-IP":null,//double,输入电功率，单位KW,用户输入，选填，默认null
			"CH-MAX-OP":null,//dobulw，输出制冷功率，单位冷吨，用户输入，选填，默认null
		},
 * @sn: 冷机编号
 * @hd: 设备所在链表头
 */
static void _extract_cpo_ch_vp(struct json_object *jele, int sn, struct list_head *hd)
{
    double ch_real_ip;
    double ch_max_op;

    struct json_object *jip;
    jip = json_object_object_get(jele, "CH-REAL-IP");
    if(json_object_get_type(jip) == json_type_double){
        ch_real_ip = json_object_get_double(jip);
    }else{
        ch_real_ip = -1;
    }

    struct json_object *jop;
    jop = json_object_object_get(jele, "CH-MAX-OP");
    if(json_object_get_type(jop) == json_type_double){
        ch_max_op = json_object_get_double(jop);
    }else{
        ch_max_op = -1;
    }

    ch_dev_t *pos;
    /* 找到设备, 配置变频变量 */
    list_for_each_entry(pos, hd, list){
        if(sn == pos->sn){
            pos->ch_real_ip = ch_real_ip;
            pos->ch_max_op = ch_max_op;
        }
    }
}

/* 提取冷机的虚点
 * @jch: 设备
 * @hd: 设备所在链表头
 */
static void extract_cpo_ch_vp(struct json_object *jch, struct list_head *hd)
{
    int num;  // 冷机个数
    num = json_object_array_length(jch);

    int i;
    struct json_object *jele;

    for(i=0; i<num; i++){
        jele = json_object_array_get_idx(jch, i);
        _extract_cpo_ch_vp(jele, i, hd);
    }
}

/* 从function object 获取虚点位
 * 目前function object 下包含：5种成员, 分别解析他们
 * "SYS": {
 * },
 * "CHILLER": [
 * ],
 * "PCHWP": [
 * ],
 * "CWP": [
 * ],
 * "CT": [
 * ],
 * 
 */
static void extract_cpo_vp(struct json_object *jfunc)
{
    pthread_mutex_lock(&cpo_dev->lock);

    json_object_object_foreach(jfunc,key,jval){
        if(strcmp(key, "SYS") == 0){
            extract_cpo_vpg(jval, &cpo_dev->sys_vp);

        }else if(strcmp(key, "CHILLER") == 0){
            extract_cpo_ch_vp(jval, &cpo_dev->ch_head.list);

        }else if(strcmp(key, "PCHWP") == 0){
            extract_cpo_wp_vp(jval, &cpo_dev->pchwp_head.list, "PCHWP");

        }else if(strcmp(key, "CWP") == 0){
            extract_cpo_wp_vp(jval, &cpo_dev->cwp_head.list, "CWP");

        }else if(strcmp(key, "CT") == 0){
            extract_cpo_ct_vp(jval, &cpo_dev->ct_head.list);
        }
    }
    pthread_mutex_unlock(&cpo_dev->lock);
}

/* 从data object 添加设备 */
static int _add_cpo_dev_from_json(struct json_object *jdata)
{
    struct json_object *jfunc, *jcfg;

    /* get data/function object, return if unexist */
    if( !json_object_object_get_ex(jdata, "function", &jfunc) ){
        return ErrNoJsonObj;
    }

    /* get data/configuration object, return if unexist */
    if( !json_object_object_get_ex(jdata, "configuration", &jcfg) ){
        return ErrNoJsonObj;
    }

    add_cpo_dev_by_rp(jcfg);
    extract_cpo_vp(jfunc);

    return Success;
}

/* 解析json, 并添新设备 */
static int add_cpo_dev_from_json(struct json_object *jroot)
{
    struct json_object *jdata, *jcfg, *jvir_dev_tp;
    if(!json_object_object_get_ex(jroot, "data", &jdata)){
        return ErrNoJsonObj;
    }
    if(!json_object_object_get_ex(jdata, "configuration", &jcfg)){
        return ErrNoJsonObj;
    }
    if(!json_object_object_get_ex(jcfg, "virDevType", &jvir_dev_tp)){
        return ErrNoJsonObj;
    }
    /* 只添加CPO设备 */
    if(strcmp(APPNAME, json_object_get_string(jvir_dev_tp)) != 0){
        return ErrNoDev;
    }

    struct json_object *jdk;

    /* 获取虚点的deviceKey，并作为当前设备实例的deviceID */
    jdk = json_object_object_get(jdata, "deviceKey");
    /* 判重 */
    if(strcmp(cpo_dev->device_id, json_object_get_string(jdk)) == 0){
        ES_PRT_ERROR("CPO: repeat device key when add new device, old_deviceKey[%s], new_deviceKey[%s] \n", \
                        cpo_dev->device_id, json_object_get_string(jdk));
        return ErrExist;
    }
    strcpy(cpo_dev->device_id, json_object_get_string(jdk));
    ES_PRT_DEBUG("Cpo: new deivceID = %s \n", cpo_dev->device_id);

    _add_cpo_dev_from_json(jdata);

    return Success;
}

/* 向json报文中添加errcode */
void add_errcode2json(struct json_object *jroot, enum error_code ec)
{
    struct json_object *jec;

    jec = json_object_new_int(ec);
    json_object_object_add(jroot, "errorCode", jec);
}

/* json value 赋值给设备描述符的点位 */
void assign_jsonval_2_pointprop(struct json_object *jval, point_prop_t *pp)
{
    enum json_type tp;
    tp = json_object_get_type(jval);

    switch(tp){
        case json_type_int:
            pp->tag = TypeOfVal_INT;
            pp->val.i = json_object_get_int(jval);
            break;
        case json_type_boolean:
            pp->tag = TypeOfVal_BOOL;
            pp->val.b = json_object_get_boolean(jval);
            break;
        case json_type_double:
            pp->tag = TypeOfVal_DOUBLE;
            pp->val.d = json_object_get_double(jval);
            break;
    
        default:
            break;
    }
}

/* 根据deviceKey和function找到点位，更新设备描述符的单个点位的value 
 *  @key      @jval
 *    |         |
 * "TEMP-ERR": 1.0
 *  
 *   @key      @jval
 *    |         |
 * "ch-23142": 10.2
 *
 */
void update_point_2devdp(char *dk, char *key, struct json_object *jval)
{
    ES_PRT_INFO("%s, %s, %s \n", dk, key, json_object_to_json_string_ext(jval, JSON_C_TO_STRING_PRETTY));

    point_prop_t *pp = find_point_bydf(dk, key);

    if(pp != NULL){
        assign_jsonval_2_pointprop(jval, pp);
    }
}

/* 更设备描述符的点位value, 可能包含多个点位
 * @jroot e.g.
 * {
    "deviceKey": "1300",
    "cmd": "write",
    "function": {
        "TEMP-ERR": 1.0
        "C-CALL": false
    },
    "sno": "12345"
    }
*/
int update_points_2devdp(struct json_object *jroot)
{
    char dk[30];
    strcpy(dk, json_object_get_string(json_object_object_get(jroot, "deviceKey")));
    ES_PRT_INFO("CPO: update points dk = %s \n", dk);

    struct json_object *jfunc;
    if( !json_object_object_get_ex(jroot, "function", &jfunc) ){
         return ErrNoJsonObj;
    }

    if(json_object_get_type(jfunc) == json_type_null){
    printf("--- \n");
        return ErrNoExist;
    }

    pthread_mutex_lock(&cpo_dev->lock);
    /* 更新每个点位 */
    json_object_object_foreach(jfunc, key, jval){
        update_point_2devdp(dk, key, jval);
    }
    pthread_mutex_unlock(&cpo_dev->lock);

    return Success;
}

static void cpo_mqtt_cmd_adddevice(struct json_object *jroot)
{
    struct json_object *jchan;
    enum error_code err;
    const char *msg_ext;

    /* 提取 channel 字段, 不是 "channel":"virDev" 的报文不处理 */
    if( json_object_object_get_ex(jroot, "channel", &jchan) ){
        if(strcmp(json_object_get_string(jchan), "virDev") != 0){
            ES_PRT_INFO("CPO: \"channel\" !=  virDev when addDevice. return. \n");
            return;
        }
    }else{
        ES_PRT_INFO("CPO: No \"channel\" when addDevice. return. \n");
        return;
    }

    /* 添加设备 */
    err = add_cpo_dev_from_json(jroot);

    if(err == Success){
        /* TODO: save to local file */
        cpo_dev->add_dev = true;
    }

    /* 加上errcode */
    add_errcode2json(jroot, err);
    msg_ext = json_object_to_json_string_ext(jroot, JSON_C_TO_STRING_PRETTY);

    ES_PRT_INFO("CPO: Send new device to /local/app/bacnet/command ... \n");
    mosquitto_publish(mqtt_cpo, NULL, "/local/app/bacnet/command", strlen(msg_ext), msg_ext, 0, 0);

    if(err != Success){
        return;
    }

    char buf[100];
    /* /local/gatewaydeviceKey/VirPointdeviceKey/command, 用于接收write 虚点的报文 */
    sprintf(buf, "%s/%s/command", MQTT_SUB_VIRPOINT_PRE, cpo_dev->device_id);
    mosquitto_subscribe(mqtt_cpo, NULL, buf, 0);

    /* 根据冷机台数，确定子状态机 */
    cpo_fsm_t *subfsm_nd;
    int index;
    for(index=0; index<cpo_dev->ch_head.ch_num; index++){
        subfsm_nd = malloc(sizeof(cpo_fsm_t)*1);
        subfsm_nd->fsmset = cpo_subfsmset;
        subfsm_nd->fsmno = index;
        subfsm_nd->bind_dev.ch_sn = index;
        subfsm_nd->bind_dev.ct_sn = -1;
        subfsm_nd->bind_dev.cwp_sn = -1;
        subfsm_nd->bind_dev.pchwp_sn = -1;
        subfsm_nd->cur_state = S_SUBSYS_INIT;
        INIT_LIST_HEAD(&subfsm_nd->v_clist);
        INIT_LIST_HEAD(&subfsm_nd->v_slist);
        INIT_LIST_HEAD(&subfsm_nd->wp_clist);
        INIT_LIST_HEAD(&subfsm_nd->wp_slist);
        INIT_LIST_HEAD(&subfsm_nd->ct_clist);
        INIT_LIST_HEAD(&subfsm_nd->ct_slist);
        list_add_tail(&subfsm_nd->list, &cpo_subfsm_hd->list);
    }
}

static void cpo_mqtt_cmd_write(struct json_object *jroot)
{
    /* 如果还没有addDevice就不做处理 */
    if(cpo_dev->add_dev == false){
        return;
    }

    enum error_code err;

    /* 更新设备描述符的点位value */
    err = update_points_2devdp(jroot);
    add_errcode2json(jroot, err);

    /* del cmd 对象 */
    const char *msg_ext;
    json_object_object_del(jroot, "cmd");
    msg_ext = json_object_to_json_string_ext(jroot, JSON_C_TO_STRING_PRETTY);

    if(err == Success){
        /* TODO: save to local file */
    }

    char dk[30];
    strcpy(dk, json_object_get_string(json_object_object_get(jroot, "deviceKey")));
    char tpc[100];
    /* topic = /local/gatewaydeviceKey/<deviceKey> */
    sprintf(tpc, "%s/%s", MQTT_PUB_PREFIX, dk);
    mosquitto_publish(mqtt_cpo, NULL, tpc, strlen(msg_ext), msg_ext, 0, 0);
}

static void cpo_del_dev(const char *dk)
{
    data_type_u val;

    if( strcmp(dk, cpo_dev->device_id) == 0 ){
        /* 1. 关掉系统 */
        /* 线程：等待删除操作完成后，删除定时器 */
        pthread_create(&tid_do_after_stop, NULL, cpo_deltmr_after_stop_thread, NULL);

        val.b = false;
        setval_2devdp_byname("C-CALL", &cpo_dev->sys_vp, val, TypeOfVal_BOOL);
        es_timer_add(TMR_DELDEVICE, TMO_DELDEVICE, TMO_DELDEVICE, cpo_to_del_device, NULL);

    }
}

void del_pchwp_dev()
{
    /* 复位水泵头 */
    cpo_dev->pchwp_head.pchwp_on_cnt = 0;
    cpo_dev->pchwp_head.pchwp_num = 0;

    /* free每个初级冷冻水泵 */
    wp_dev_t *pchwp_pos, *n;
    list_for_each_entry_safe(pchwp_pos, n, &cpo_dev->pchwp_head.list, list){
        free(pchwp_pos->wp_prop.points);
        list_del(&pchwp_pos->list);
        free(pchwp_pos);
    }
}

void del_cwp_dev()
{
    /* 复位水泵头 */
    cpo_dev->cwp_head.cwp_on_cnt = 0;
    cpo_dev->cwp_head.cwp_num = 0;

    /* free每个冷却水泵 */
    wp_dev_t *cwp_pos, *n;
    list_for_each_entry_safe(cwp_pos, n, &cpo_dev->cwp_head.list, list){
        free(cwp_pos->wp_prop.points);
        list_del(&cwp_pos->list);
        free(cwp_pos);
    }
}

void del_ct_dev()
{
    /* 复位冷却塔头 */
    cpo_dev->ct_head.ct_on_cnt = 0;
    cpo_dev->ct_head.ct_num = 0;
    free(cpo_dev->ct_head.ctg_rp.points);
    memset(&cpo_dev->ct_head.ctg_rp, 0, sizeof(dev_prop_t));

    ct_dev_t *ct_pos, *nct;
    ctf_dev_t *ctf_pos, *nctf;
    /* free 所有冷却塔 */
    list_for_each_entry_safe(ct_pos, nct, &cpo_dev->ct_head.list, list_ct){

        /* free 一个冷却塔内所有风扇 */
        list_for_each_entry_safe(ctf_pos, nctf, &ct_pos->list_ctf_hd, list){
            free(ctf_pos->ctf_prop.points);
            list_del(&ctf_pos->list);
            free(ctf_pos);
        }
        list_del(&ct_pos->list_ct);
        free(ct_pos);
    }
}

void del_ch_dev()
{
    /* 复位冷机头 */
    free(cpo_dev->ch_head.chg_rp.points);
    memset(&cpo_dev->ch_head.chg_rp, 0, sizeof(dev_prop_t));
    cpo_dev->ch_head.ch_num = 0;
    cpo_dev->ch_head.ch_on_cnt = 0;
    cpo_dev->ch_head.add_tm = 0;
    cpo_dev->ch_head.add_flag = false;
    cpo_dev->ch_head.sub_tm = 0;
    cpo_dev->ch_head.sub_flag = false;

    /* free每个冷机 */
    ch_dev_t *ch_pos, *n;
    list_for_each_entry_safe(ch_pos, n, &cpo_dev->ch_head.list, list){
        free(ch_pos->ch_prop.points);  // 释放冷机实点
        list_del(&ch_pos->list);
        free(ch_pos);  // free冷机节点
    }

}

void del_cpo_dev()
{
    del_ch_dev();
    del_cwp_dev();
    del_pchwp_dev();
    del_ct_dev();

    memset(cpo_dev->device_id, 0, sizeof(cpo_dev->device_id));
    /* free 系统实点 */
    free(cpo_dev->sys_rp.points);
    memset(&cpo_dev->sys_rp, 0, sizeof(dev_prop_t));
    /* free 系统虚点 */
    free(cpo_dev->sys_vp.points);
    memset(&cpo_dev->sys_vp, 0, sizeof(dev_prop_t));

    cpo_dev->add_dev = false;
}

static void cpo_mqtt_cmd_delDevice(struct json_object *jroot)
{
    /* 添加过设备才能删除 */
    if(cpo_dev->add_dev == false){
        return;
    }
    struct json_object *jchan;

    /* 提取 channel 字段, 不是 "channel":"virDev" 的报文不处理 */
    if( json_object_object_get_ex(jroot, "channel", &jchan) == false ){
        return;
    }

    if(strcmp(json_object_get_string(jchan), "virDev") != 0){
        return;
    }

    struct json_object *jdata;
    struct json_object *jvirtp;
    struct json_object *jdk;
    const char *dk;
    
    /* 
     * 处理如下报文(部分)
     * "data": {
     *      "virDevType": "CPO",
     * }
     */
    if( json_object_object_get_ex(jroot, "data", &jdata) == false ){
        return;
    }

    if( json_object_object_get_ex(jdata, "virDevType", &jvirtp) == false ){
        return;
    }
            
    if(strcmp(json_object_get_string(jvirtp), "CPO") != 0){
        return;
    }

    if( json_object_object_get_ex(jdata, "deviceKey", &jdk) == false ){
        return;
    }

    dk = json_object_get_string(jdk);
    /* 根据device_key找到设备，然后删除它，因为只有一个设备，
    所以比对device_key和cpo_dev->device_id是否一致即可 */
    cpo_del_dev(dk);
}

/* 处理topic中带command字段的mqtt payload, /local/gatewaydeviceKey/xxx/command
 * @Msg: mqtt raw payload 
 * @Topic: Mqtt topic
 */
void cpo_mqtt_cmd_msg_proc(char *msg, char *topic)
{
    const char *cmd;
    struct json_object *jroot, *jcmd_val;

    /* string -> json */
    jroot = json_tokener_parse(msg);

    /* 提取 cmd 字段，没有cmd 字段的报文不处理  */
    if( !json_object_object_get_ex(jroot, "cmd", &jcmd_val) ){
        ES_PRT_INFO("CPO: Rcv command topic json with no \"cmd\". return. \n");
        return;
    }

    cmd = json_object_get_string(jcmd_val);

    if(strcmp(cmd, "addDevice") == 0){
        cpo_mqtt_cmd_adddevice(jroot);

    }else if(strcmp(cmd, "write") == 0){
        cpo_mqtt_cmd_write(jroot);
    
    }else if(strcmp(cmd, "delDevice") == 0){
        cpo_mqtt_cmd_delDevice(jroot);
    }

//    print_all_points();
    json_object_put(jroot);
}

/* 处理topic: /local/gatewaydeviceKey/+ 的payload */
void cpo_mqtt_msg_proc(char *msg)
{
    /* 如果还没有addDevice就不做处理 */
    if(cpo_dev->add_dev == false){
        return;
    }

    enum error_code err;
    struct json_object *jroot;

    /* string -> json */
    jroot = json_tokener_parse(msg);
    ES_PRT_DEBUG("%s \n", json_object_to_json_string_ext(jroot, JSON_C_TO_STRING_PRETTY));

    /* 不处理带cmd字段的json */
    struct json_object *jcmd;
    if( json_object_object_get_ex(jroot, "cmd", &jcmd) ){
        return;
    }

    err = update_points_2devdp(jroot);

//    print_all_points();
    json_object_put(jroot);
}